package com.hhwy.framework.persistent.impl;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.hhwy.framework.common.Parameter;
import com.hhwy.framework.persistent.DAO;
import com.hhwy.framework.persistent.Pagination;
import com.hhwy.framework.persistent.QueryResult;
import com.hhwy.framework.persistent.SqlDao;
import com.hhwy.framework.persistent.VOCache;
import com.hhwy.framework.persistent.entity.DProperty;
import com.hhwy.framework.persistent.entity.Domain;
import com.hhwy.framework.persistent.entity.DomainInfo;
import com.hhwy.framework.persistent.query.IWhereClause;
import com.hhwy.framework.util.PlatformTools;

/**
 * <b>类 名 称：</b>DaoSupport 通用的JPA Dao<br/>
 * <b>类 描 述：</b><br/>
 * <b>创 建 人：</b>lihuiming<br/>
 * <b>修 改 人：</b>Administrator<br/>
 * <b>修改时间：</b>2014年8月4日 上午8:55:44<br/>
 * <b>修改备注：</b><br/>
 * 
 * @version 1.0.0<br/>
 */
@Repository("daoSupport")
@Scope("singleton")
public class DaoSupport<T> implements DAO<T> {
	Logger log = LoggerFactory.getLogger(DaoSupport.class);

	@PersistenceContext
	@Qualifier(value = "entityManagerFactory")
	private EntityManager em;

	@SuppressWarnings("rawtypes")
	@Autowired
	SqlDao sqlDao;
	
	@SuppressWarnings({ "hiding", "unchecked" })
	@Override
	public <T> T find(String jpql, Object[] params) {
		Query query = em.createQuery(jpql);
		setParams(query, params);
		Object obj = getVOList(query.getResultList(), null);

		return (T) obj;
	}
	@SuppressWarnings({ "hiding", "unchecked" })
	@Override
	public <T> T findOne(String jpql, Object[] params) {
		Query query = em.createQuery(jpql);
		setParams(query, params);
		List<?> list = query.getResultList();
		if(list.size() > 0)
			return (T) list.get(0);
		else
			return null;
	}


	
	
	@SuppressWarnings({ "unchecked", "hiding", "rawtypes" })
	@Override
	public <T> T find(Class<? extends Domain> entityClass, Class clazzVO, IWhereClause where) {
		Query query = getQuery(entityClass, where);
		
		Object obj = getVOList(query.getResultList(), clazzVO);

		return (T) obj;
	}

	@SuppressWarnings({ "unchecked", "hiding" })
	public <T> T findEntity(String jpql , Object[] params){
		Query query = em.createQuery(jpql);
		setParams(query, params);
		return (T) query.getResultList();
		
	}

	@SuppressWarnings({ "unchecked", "hiding" })
	@Override
	public <T> T findEntity(Class<? extends Domain> entityClass, IWhereClause where) {
		Query query = getQuery(entityClass, where);
		
		List<T> list = query.getResultList();

		return (T) list;
	}


	/**
	 * getQuery(描述这个方法的作用)<br/>
	 * @param entityClass
	 * @param where
	 * @return 
	 * Query
	 * @exception 
	 * @since  1.0.0
	*/
	private Query getQuery(Class<? extends Domain> entityClass, IWhereClause where) {
		String entityname = getEntityName(entityClass);
		String whereStr = "";
		List<Map<String, Object>> queryParams = null;
		if (where != null) {
			String whereJpql = where.whereJpql();
			queryParams = where.queryParams();

			whereStr = (StringUtils.isBlank(whereJpql) ? "" : "where "
					+ whereJpql)
					+ buildOrderby(where.orderParams());
		}
		Query query = em.createQuery("select o from " + entityname + " o "
				+ whereStr);
		if (queryParams != null) {
			setParams(query, queryParams);
		}
		return query;
	}
	/**
	 * getVO(描述这个方法的作用)<br/>
	 * 
	 * @param resultList
	 * @return List<T>
	 * @exception
	 * @since 1.0.0
	 */
	@SuppressWarnings({ "hiding", "unchecked" })
	private <T>T getVOList(List<T> resultList, Class<?> clazzVO ) {
		List<T> list = null;
		if (resultList != null && resultList.size() > 0) {
			Class<?> voClass = clazzVO!= null ? clazzVO: getVOClass((Domain) resultList.get(0));

			if (voClass == null)
				list = resultList;
			else {

				DomainInfo domainInfo = getDomainInfo(voClass);
				list = new ArrayList<T>();
				for (T di : resultList) {
					try {

						Object vo = getVO(voClass, (Domain) di, domainInfo);
						list.add((T) vo);
					} catch (Exception e) {
						System.out.println("getVO:::检查字典异常！" + e.getMessage());
					}
				}
			}
		}
		if(list != null)
			return (T) list;
		else
			return (T) resultList;
	}

	/**
	 * getDomainInfo(描述这个方法的作用)<br/>
	 * 
	 * @param voClass
	 * @return DomainInfo
	 * @exception
	 * @since 1.0.0
	 */
	private DomainInfo getDomainInfo(Class<?> voClass) {
		DomainInfo domainInfo = null;
		try {
			domainInfo = VOCache.getDomainInfo(voClass);
		} catch (InstantiationException | IllegalAccessException e1) {
			e1.printStackTrace();
		}
		return domainInfo;
	}

	private Class<?> getVOClass(Domain di) {
		String className = di.getClass().getName() + "VO";
		Class<?> voClass = hasClass(className);

		return voClass;

	}

	@SuppressWarnings({ "hiding", "unchecked" })
	private <T> T getVO(Class<?> voClass, Domain po, DomainInfo domainInfo)
			throws Exception {
		Object vo = voClass.newInstance();
		BeanUtils.copyProperties(vo, po);
		/*checkDict(vo ,  domainInfo);*/
		return (T) vo;
	}

	private Class<?> hasClass(String className) {
		Class<?> voClass = null;
		try {
			voClass = Class.forName(className);
		} catch (ClassNotFoundException e1) {
			System.out.println("getVO:::未找到VO类:::" + e1.getMessage());
		}
		return voClass;
	}

	/**
	 * checkDict(描述这个方法的作用)<br/>
	 * 
	 *            void
	 * @throws NoSuchMethodException
	 * @throws InvocationTargetException
	 * @throws IllegalAccessException
	 * @exception
	 * @since 1.0.0
	 */
	/*private void checkDict(Object vo, DomainInfo domainInfo)
			throws IllegalAccessException, InvocationTargetException,
			NoSuchMethodException {
		Iterator<String> it = domainInfo.propertyMap.keySet().iterator();
		while (it.hasNext()) {
			String propertyName = it.next();
			DProperty property = domainInfo.propertyMap.get(propertyName);
			if (property.getDict() != null && property.getDict().length() > 0) {
				String dictName = property.getDict();
				String itemCode = BeanUtils.getProperty(vo, propertyName);
				if (itemCode != null) {
					String itemName = DictHelper.getDict(dictName).getItemName(
							itemCode);
					BeanUtils.setProperty(vo, propertyName, itemName);
				}
			}

		}
	}*/

	public void save(Object entity) {
		Domain domain = (Domain) entity;
		if (domain.getId() == null)
			domain.setId(PlatformTools.getID());
		checkDomain(domain);
		sqlDao.saveEntityBySQL(entity);
	}
	 
	@Override
	public void saveExcludeNull(Object entity) {
		Domain domain = (Domain) entity;
		if (domain.getId() == null)
			domain.setId(PlatformTools.getID());
		checkDomain(domain);
		sqlDao.saveExcludeNull(entity);
	}
	
	public void insert(Object entity) {
		Domain domain = (Domain) entity;
		if (domain.getId() == null)
			domain.setId(PlatformTools.getID());
		checkDomain(domain);
		sqlDao.insertEntityBySQL(entity);
		
	}
	public void update(Object entity) {
		Domain domain = (Domain) entity;
		if (StringUtils.isNotEmpty(domain.getId())) {
			checkDomain((Domain) entity);
			sqlDao.updateEntityBySQL(entity);
		}
	}
	public void updateExcludeNull(Object entity){
		Domain domain = (Domain) entity;
		if (StringUtils.isNotEmpty(domain.getId())) {
			checkDomain((Domain) entity);
			sqlDao.updateExcludeNull(entity);
		}
		
	}

	/**
	 * checkDomain(检查实体对象，设置公有属性)<br/>
	 * 
	 * @param domain
	 *            void
	 * @exception
	 * @since 1.0.0
	 */
	private void checkDomain(Domain domain) {

		Timestamp t = new Timestamp(System.currentTimeMillis());
		if (domain.getCreateTime() == null) {
			domain.setCreateTime(t);
			domain.setUpdateTime(t);
		} else {
			domain.setUpdateTime(t);
		}
		String userId = Parameter.userId.get();
		if (domain.getCreateUser() == null) {
			domain.setCreateUser(userId);
			domain.setUpdateUser(userId);
		} else {

			domain.setUpdateUser(userId);
		}

	}

	public void saveList(List<?> list) {

		for (int i = 0; i < list.size(); i++) {
			Object o = list.get(i);
			checkDomain((Domain) o);
			sqlDao.saveEntityBySQL(o);
		}
	}
	 
	@Override
	public void insertList(List<?> list) {
		for (int i = 0; i < list.size(); i++) {
			Object o = list.get(i);
			checkDomain((Domain) o);
			sqlDao.insertEntityBySQL(o);
		}
	}
	public int delete(Object entity) {
		int result = 1;
		if(entity ==  null)
			return 0;
		
		try {
			sqlDao.deleteEntityBySQL(entity);
		} catch (Exception e) {
			e.printStackTrace();
			result = 0;
		}
		log.info("删除数据,domain=[" + entity.getClass().getName() + "], id=["
				+ ((Domain) entity).getId() + "],toString=["
				+ entity.toString() + "]");
		return result;
	}

	public int deleteList(List<?> list) {
		int count = 0;
		for (Object entity : list) {
			sqlDao.deleteEntityBySQL(entity);
			log.info("删除数据,domain=[" + entity.getClass().getName() + "], id=["
					+ ((Domain) entity).getId() + "],toString=["
					+ entity.toString() + "]");
			count++;
		}
		return count;
	}

	public void delete(String[] ids, Class<?> clazz) {

		if (ids != null && ids.length > 0) {
			for (int i = 0; i < ids.length; i++) {
				try {
					sqlDao.deleteEntityBySQL(ids[i],clazz.newInstance());
				} catch (InstantiationException | IllegalAccessException e) {
					e.printStackTrace();
				}
			}
		}
	}

	@Deprecated
	@Transactional(propagation = Propagation.REQUIRED)
	public int executeJPQL(String jpql, Object[] params) {

		Query query = em.createQuery(jpql);
		setParams(query, params);
		return query.executeUpdate();
	}

	/**
	 * 注意代码修改一定要确定没有问题了再提交
	 */
	@SuppressWarnings({ "hiding", "unchecked", "rawtypes" })
	@Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
	public <T> QueryResult<T> getPage(Class<T> entityClass, IWhereClause where) {
		int pageNo = 1, pageSize = 1;
		if (Pagination.pageNo.get() != null) {
			pageNo = Pagination.pageNo.get();
		}
		if (Pagination.pageSize.get() != null) {
			pageSize = Pagination.pageSize.get();
		}
		QueryResult<T> qr = new QueryResult<T>();
		String entityname = getEntityName(entityClass);
		String whereStr = "";
		String whereStrCount = "";
		List<Map<String, Object>> queryParams = null;
		if (where != null) {
			String whereJpql = where.whereJpql();
			queryParams = where.queryParams();

			whereStr = (StringUtils.isBlank(whereJpql) ? "" : "where "
					+ whereJpql)
					+ buildOrderby(where.orderParams());
			whereStrCount = (StringUtils.isBlank(whereJpql) ? "" : "where "
					+ whereJpql);
		}
		// 分页参数为空不进行数据查询
		if (Pagination.pageSize.get() != null) {
			Query query = em.createQuery("select o from " + entityname + " o "
					+ whereStr);
			if (queryParams != null) {
				setParams(query, queryParams);
			}
			int firstIndex = (pageNo - 1) * pageSize;
			if (firstIndex != -1)
				query.setFirstResult(firstIndex).setMaxResults(pageSize);
			qr.setData((List<T>) getVOList(query.getResultList(), null));
		}else
			qr.setData(new ArrayList());
		Query query = em.createQuery("select count(o.id) from " + entityname
				+ " o " + whereStrCount);
		if (queryParams != null) {
			setParams(query, queryParams);
		}
		qr.setTotal((Long) query.getSingleResult());
		return qr;
	}

	private String getEntityName(Class<?> clazz){
		String name = null;
		Annotation[] annos = clazz.getAnnotations();
		for (int i = 0; i < annos.length; i++) {
			Annotation anno = annos[i];
			
			if(anno.annotationType().equals(Entity.class)){
				
				name = ((Entity)anno).name();
			}
		}
		return name;
	}
	/**
	 * setParams(描述这个方法的作用)<br/>
	 * 
	 * @param query
	 * @param queryParams
	 *            void
	 * @exception
	 * @since 1.0.0
	 */
	private void setParams(Query query, List<Map<String, Object>> queryParams) {
		for (Map<String, Object> paraMap : queryParams) {

			String key = paraMap.keySet().iterator().next();
			query.setParameter(key, paraMap.get(key));
		}
	}

	/**
	 * 组装order by语句
	 * 
	 * @param orderby
	 * @return
	 */
	protected String buildOrderby(List<Map<String, String>> orderby) {
		String sortField = Pagination.sortField.get();
		StringBuffer orderbyql = new StringBuffer("");
		if (orderby != null && orderby.size() > 0) {
			orderbyql.append(" order by ");
			for (Map<String, String> orderMap : orderby) {
				if (orderMap == null) {
					orderbyql = new StringBuffer("");
					break;
				}
				String key = orderMap.keySet().iterator().next();
				orderbyql.append("o.").append(key).append(" ")
						.append(orderMap.get(key)).append(",");
			}
			if (orderbyql.length() > 0)
				orderbyql.deleteCharAt(orderbyql.length() - 1);

		} else if (StringUtils.isNotEmpty(sortField)) {

			String sortOrder = Pagination.sortOrder.get() == null ? "desc"
					: Pagination.sortOrder.get();
			orderbyql.append(" order by ").append("o.").append(sortField)
					.append(" ").append(sortOrder);
		}
		return orderbyql.toString();
	}

	private void setParams(Query query, Object[] params) {
		if (params != null && params.length > 0) {
			for (int i = 0; i < params.length; i++) {
				int index = i + 1;
				query.setParameter(index, params[i]);

			}
		}

	}

	@SuppressWarnings("hiding")
	public <T> T findById(String id, Class<T> clazz) {
		return em.find(clazz, id);
	}

	@SuppressWarnings({ "hiding", "unchecked" })
	@Override
	public <T> T findVOById(String id, Class<T> clazz) throws Exception {
		Domain domain = (Domain) em.find(clazz, id);
		@SuppressWarnings("rawtypes")
		Class voClass = getVOClass(domain);
		Object vo = getVO(voClass, domain, getDomainInfo(voClass));
		return (T) vo;
	}

	/**
	 * getName(获取实体的)<br/>
	 * 
	 * @param clazz
	 * @return String
	 * @exception
	 * @since 1.0.0
	 */
	private String getName(Class<?> clazz) {
		String entityName = null;
		try {
			DomainInfo di = VOCache.getDomainInfo(clazz);
			entityName = di.entityName;
		} catch (InstantiationException | IllegalAccessException e) {
			e.printStackTrace();
		}
		return entityName;
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> getBySql(String sqlKey, Map<String, Object> params) {

		return sqlDao.get(sqlKey, params);
	}
	
	@Override
	public List<T> getBySql(String sqlKey, Object parameter) {
		return sqlDao.get(sqlKey, parameter);
	}
	@SuppressWarnings("unchecked")
	@Override
	public T getOneBySQL(String sqlKey, Map<String, Object> params) {

		return (T) sqlDao.getOne(sqlKey, params);
	}
	 
	@Override
	public T getOneBySQL(String sqlKey, Object parameter) {
		return (T) sqlDao.getOne(sqlKey, parameter);
	}

	@Override
	public int saveBySql(String sqlKey, Object parameter) {

		return sqlDao.save(sqlKey, parameter);
	}

	@Override
	public int updateBySql(String sqlKey, Object parameter) {
		return sqlDao.update(sqlKey, parameter);
	}

	@Override
	public int deleteBySql(String sqlKey, Object parameter) {
		StringBuilder s = new StringBuilder("删除数据,sqlKey=[" );
		s.append(sqlKey).append("], parameter=[").append(parameter).append("]");
		if(parameter != null)
			s.append("parameter.toString=[").append(parameter.toString()).append("]");
		return sqlDao.delete(sqlKey, parameter);
	}

	public Object executeSql(String sqlKey, Object parameter){
		return sqlDao.executeSql(sqlKey, parameter);
	}
	
	public QueryResult query(String sqlKey, Map parameter){
		
		return sqlDao.query( sqlKey,  parameter);
	}




}